package com.yanyuan.snio.part;

import lombok.extern.slf4j.Slf4j;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;

/**
 * @author: yanyuan
 * @Date: 2021/3/4 11:46
 * @Description:
 *
 * 一、通道 channel：用户源节点与目标节点的连接，
 * 在java NIO中负责缓冲区传输。channel本身不存储数据，
 * 因此需要配合缓冲区进行传输。
 *
 * 二、通道的主要实现类
 * java.nio.channels.Channel接口：
 *  --FileChannel
 *  --SocketChannel
 *  --ServerSocketChannel
 *  --DatagramChannel
 *
 *  三、获取通道
 *  1、Java针对通道类提供了getChannel()方法
 *  本地IO:
 *  FileInputStream/FileOutputStream
 *  RandomAccessFile
 *
 *  网络IO
 *  Socket
 *  ServerSocket
 *  DatagramSocket
 *
 *  2、在JDK1.7中NIO.2针对各种通道提供了静态方法open()
 *  3、在JDK1.7中NIO.2的Files工具类的newByteChannel()
 */
@Slf4j
public class NioP4Channel {
    /**
     * 利用通道完成文件复制(非直接缓冲区)
     * @throws IOException
     */
    public void copyFileByGetChannel() throws IOException {
        long startTime = System.currentTimeMillis();

        FileInputStream fis = new FileInputStream("1.mp4");
        FileOutputStream fos = new FileOutputStream("2.mp4");

        //获取通道
        FileChannel inChannel = fis.getChannel();
        FileChannel outChannel = fos.getChannel();

        //分配指定大小缓存区
        ByteBuffer buf = ByteBuffer.allocate(1024);

        //3、将通道中的数据存入缓冲区
        while (inChannel.read(buf) != -1){
            //切换读取模式
            buf.flip();
            //将缓冲区的数据写入通道中
            outChannel.write(buf);
            //清空缓冲区
            buf.clear();
        }
        log.info("copy耗时{}ms", (System.currentTimeMillis() - startTime));

    }

    /**
     * 使用直接缓冲区完成文件的复制（内存映射文件）
     */
    public void copyFileByOpen() throws IOException {
        long startTime = System.currentTimeMillis();
        FileChannel inChannel = FileChannel.open(Paths.get("1.mp4"), StandardOpenOption.READ);
        FileChannel outChannel = FileChannel.open(Paths.get("2.mp4"), StandardOpenOption.WRITE, StandardOpenOption.READ, StandardOpenOption.CREATE);
        //内存映射文件
        MappedByteBuffer inMappedByteBuffer = inChannel.map(FileChannel.MapMode.READ_ONLY, 0, inChannel.size());
        MappedByteBuffer outMappedByteBuffer = outChannel.map(FileChannel.MapMode.READ_WRITE, 0, inChannel.size());
        //直接对缓冲区进行数据读写操作
        byte[] dst = new byte[inMappedByteBuffer.limit()];
        inMappedByteBuffer.get(dst);
        outMappedByteBuffer.put(dst);
        log.info("copy耗时{}ms", (System.currentTimeMillis() - startTime));
    }

    /**
     * 通道之间的传输
     */
    public void copyFileByChannel() throws IOException {
        long startTime = System.currentTimeMillis();
        FileChannel inChannel = FileChannel.open(Paths.get("1.mp4"), StandardOpenOption.READ);
        FileChannel outChannel = FileChannel.open(Paths.get("2.mp4"), StandardOpenOption.WRITE, StandardOpenOption.READ, StandardOpenOption.CREATE);
//        inChannel.transferTo(0, inChannel.size(), outChannel);
        outChannel.transferFrom(inChannel, 0, inChannel.size());
        log.info("copy耗时{}ms", (System.currentTimeMillis() - startTime));
    }

}
